/*
 * Copyright 2008-2023 dexian.vip. All rights reserved.
 * Support: http://www.dexian.vip
 * License: http://www.dexian.vip/license
 */

package vip.dexian.code.utils;

import vip.dexian.common.CommonAttributes;
import org.apache.commons.beanutils.ConvertUtilsBean;
import org.apache.commons.beanutils.Converter;
import org.apache.commons.beanutils.converters.ArrayConverter;
import org.apache.commons.beanutils.converters.DateConverter;

import java.sql.Date;
import java.time.LocalDateTime;

/**
 * 使用 apache common {@link  org.apache.commons.beanutils.ConvertUtilsBean} 将指定的值（value）转换为对应的类型。默认注册：
 * <p>
 * 1. 基本类型的包装类型（registerPrimitives）。
 * <p>
 * 2. 标准类型（registerStandard）。
 * <p>
 * 3. 其他类型（registerOther）。
 * <p>
 * 4. 数组（registerArrays）。
 *
 * @author 挺好的 2023年05月30日 16:57
 * @see org.apache.commons.beanutils.ConvertUtilsBean
 */
public final class BeanConvertUtils {

    /**
     * 类型转换工具。除了内置的一些类型转换，还可以自定义类型转换。
     */
    private static final ConvertUtilsBean CONVERT_UTILS;

    static {
        CONVERT_UTILS = new ConvertUtilsBean() {

            /**
             * 将值转换为指定类的对象（如果可能）。 如果未注册所需目标类型的转换器，则传入的对象将原样返回
             *
             * @param value
             *         值（可能为null）
             * @param targetType
             *         目标类型（不能为null）
             *
             * @return 转换后的值。{@link org.apache.commons.beanutils.ConvertUtilsBean#convert(Object)}
             */
            @SuppressWarnings ("rawtypes")
            @Override
            public Object convert (Object value, Class targetType) {
                // 如果未注册该类型
                if (!this.isRegistered(targetType)) {
                    this.doRegister(targetType, false);
                }

                return super.convert(value, targetType);
            }

            /**
             * 将指定的value转换为String。 如果指定的值为数组，则返回第一个元素（转换为字符串），如果value == null，返回null。
             *
             * @param value
             *         指定的value
             *
             * @return 转换后的值。{@link org.apache.commons.beanutils.ConvertUtilsBean#convert(Object)}
             */
            @Override
            public String convert (Object value) {

                if (value != null) {
                    Class <?> targetType = value.getClass();
                    boolean isEnumArray = this.doRegister(targetType, true);

                    if (isEnumArray) {
                        Converter converter = super.lookup(targetType);
                        return converter.convert(String.class, value);
                    }
                }

                return super.convert(value);
            }

            /**
             * 将指定值转换为指定类的对象（如果可能）。 否则，返回值的字符串表示
             *
             * @param value
             *         要转换的值。（可能为null）
             * @param clazz
             *         要转换的对象类型。（不能为null）
             *
             * @return 转换后的值。{@link org.apache.commons.beanutils.ConvertUtilsBean#convert(String, Class)}
             */
            @SuppressWarnings ("rawtypes")
            @Override
            public Object convert (String value, Class clazz) {

                if (clazz.isEnum() && super.lookup(clazz) == null) {
                    this.registerEnum(clazz);
                }

                return super.convert(value, clazz);
            }

            /**
             * 将指定值的数组转换为指定类的对象数组（如果可能）。 如果指定的 Clazz 本身是数组类型，则返回值的类型为Clazz类型。否则，将构造一个数组，其类型为Clazz类型。
             *
             * @param values
             *         要转换的值数组
             * @param clazz
             *         要转换为的数组或类的类型（不得为 null）
             *
             * @return 转换后的值。{@link org.apache.commons.beanutils.ConvertUtilsBean#convert(String[], Class)}
             */
            @SuppressWarnings ("rawtypes")
            @Override
            public Object convert (String[] values, Class clazz) {
                if (clazz.isArray() && clazz.getComponentType().isEnum() && super.lookup(
                        clazz.getComponentType()) == null) {
                    super.register(new EnumConverter(clazz.getComponentType()), clazz.getComponentType());
                }
                return super.convert(values, clazz);
            }

            /**
             * 是否注册
             *
             * @param targetType
             *         目标类型
             *
             * @return {@link org.apache.commons.beanutils.ConvertUtilsBean#lookup(Class)} ，如果没有注册返回false，否则返回true
             */
            private boolean isRegistered (Class targetType) {
                return super.lookup(targetType) != null;
            }

            /**
             * 注册枚举
             *
             * @param targetType
             *         目标类型
             */
            private void registerEnum (Class targetType) {
                super.register(new EnumConverter(targetType), targetType);
            }

            /**
             * 注册枚举数组
             *
             * @param targetType
             *         目标类型
             */
            private void registerEnumArray (Class targetType) {
                ArrayConverter arrayConverter = new ArrayConverter(targetType,
                        new EnumConverter(targetType.getComponentType()), 0
                );
                arrayConverter.setOnlyFirstToString(false);
                super.register(arrayConverter, targetType);
            }

            /**
             * 注册普通枚举以及枚举数组
             *
             * @param targetType
             *         目标类型
             * @param isValidEnumArrayRegistered
             *         是否校验枚举数组是否注册过
             *
             * @return 如果 isValidEnumArrayRegistered == true， 返回true，表示注册的是枚举数组
             */
            private boolean doRegister (Class targetType, boolean isValidEnumArrayRegistered) {

                if (targetType.isEnum()) {
                    this.registerEnum(targetType);

                    return false;

                } else if (targetType.isArray() && targetType.getComponentType().isEnum()) {
                    if (isValidEnumArrayRegistered && !this.isRegistered(targetType)) {
                        this.registerEnumArray(targetType);
                    }

                    return true;
                }

                return false;
            }

        };

        DateConverter dateConverter = new DateConverter();
        dateConverter.setPatterns(CommonAttributes.DatePattern.PATTERNS);
        CONVERT_UTILS.register(dateConverter, Date.class);

        LocalDateTimeConverter localDateTimeConverter = new LocalDateTimeConverter();
        CONVERT_UTILS.register(localDateTimeConverter, LocalDateTime.class);
    }

    public static ConvertUtilsBean getConvertUtils () {
        return CONVERT_UTILS;
    }
}
